﻿using Microsoft.AspNetCore.Mvc.Filters;
using APIGATEWAY_SDK;
using System.Net;
using Microsoft.AspNetCore.Http;
using HttpRequest = APIGATEWAY_SDK.HttpRequest;
using System.Collections.Generic;
using System;
using Microsoft.AspNetCore.Mvc;
using System.Text.RegularExpressions;
using System.IO;
using System.Text;
using System.Globalization;
using System.Threading.Tasks;

namespace backend_signature.Filters
{
    public class ApigatewaySignatureFilter : Attribute, IAsyncAuthorizationFilter
    {
        private Dictionary<string, string> secrets = new Dictionary<string, string>
        {
            // 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险，建议在配置文件或者环境变量中密文存放，使用时解密，确保安全；
            // 本示例以ak和sk保存在环境变量中为例，运行本示例前请先在本地环境中设置环境变量HUAWEICLOUD_SDK_AK1和HUAWEICLOUD_SDK_SK1, HUAWEICLOUD_SDK_AK2和HUAWEICLOUD_SDK_SK2。
            {Environment.GetEnvironmentVariable("HUAWEICLOUD_SDK_AK1"), Environment.GetEnvironmentVariable("HUAWEICLOUD_SDK_SK1") },
            {Environment.GetEnvironmentVariable("HUAWEICLOUD_SDK_AK2"), Environment.GetEnvironmentVariable("HUAWEICLOUD_SDK_SK2") },
        };
        private Regex authorizationPattern = new Regex("SDK-HMAC-SHA256\\s+Access=([^,]+),\\s?SignedHeaders=([^,]+),\\s?Signature=(\\w+)");
        private const string BasicDateFormat = "yyyyMMddTHHmmssZ";

        public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            var request = context.HttpContext.Request;
            string authorization = request.Headers["Authorization"];
            if (authorization == null)
            {
                context.Result = new UnauthorizedResult();
                return;
            }
            var matches = authorizationPattern.Matches(authorization);
            if (matches.Count == 0)
            {
                context.Result = new UnauthorizedResult();
                return;
            }
            var groups = matches[0].Groups;
            string key = groups[1].Value;
            if (!secrets.ContainsKey(key))
            {
                context.Result = new UnauthorizedResult();
                return;
            }
            string secret = secrets[key];
            string[] signedHeaders = groups[2].Value.Split(';');

            HttpRequest sdkRequest = new HttpRequest(request.Method,
                 new Uri(request.Scheme + "://" + request.Host.Value + request.Path + request.QueryString));
            WebHeaderCollection headers = new WebHeaderCollection();
            string dateHeader = null;
            bool needBody = true;
            foreach (var h in signedHeaders)
            {
                var value = request.Headers[h];
                headers[h] = value;
                if (h.ToLower() == "x-sdk-date")
                {
                    dateHeader = value;
                }
                if (h.ToLower() == "x-sdk-content-sha256" && value == "UNSIGNED-PAYLOAD")
                {
                    needBody = false;
                }
            }
            sdkRequest.headers = headers;
            if (dateHeader == null)
            {
                context.Result = new UnauthorizedResult();
                return;
            }
            DateTime t = DateTime.ParseExact(dateHeader, BasicDateFormat, CultureInfo.CurrentCulture);
            if (Math.Abs((t - DateTime.Now).Minutes) > 15)
            {
                context.Result = new UnauthorizedResult();
                return;
            }
            if (needBody)
            {
                request.EnableBuffering();
                using (StreamReader reader = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true))
                {
                    sdkRequest.body = await reader.ReadToEndAsync();
                }
                request.Body.Position = 0;
            }
            Signer signer = new Signer();
            signer.Key = key;
            signer.Secret = secret;
            if (!signer.Verify(sdkRequest, groups[3].Value))
            {
                context.Result = new UnauthorizedResult();
            }
        }
    }
}